home *** CD-ROM | disk | FTP | other *** search
/ 9-Digit Zip Code Directory / 9-Digit Zip Code Directory (American Business Information) (ABIZIP-12).ISO / z4src.zip / Z4PARSE.CPP < prev    next >
C/C++ Source or Header  |  1993-08-22  |  32KB  |  1,220 lines

  1. //----------------------------------------------------------------------------
  2. //                            MODULE DESCRIPTION
  3. //
  4. //  Module:    z4parse.cpp
  5. //   Title:    ZIP+4 Engine
  6. //  Notice:    John M. Weeder
  7. //                 Copyright (c) 1993. All rights reserved.
  8. //             This module contains proprietary information and should be 
  9. //                treated as confidential.
  10. //
  11. //----------------------------------------------------------------------------
  12. //                           MAINTENANCE HISTORY
  13. //
  14. // $Workfile$
  15. // $Revision$
  16. //   $Author$
  17. //     $Date$
  18. //      $Log$    
  19. //
  20. //----------------------------------------------------------------------------
  21. //                             MODULE NARRATIVE
  22. //
  23. //    This module contains code for the class Z4_INQ.
  24. //
  25. //    The code in this module may be written in C++ or C.
  26. //
  27. //    This module is portable to:
  28. //        DOS 3.X+
  29. //        MS Windows 3.X+
  30. //        OS/2 2.X+
  31. //        OS/2 2.0 PM
  32. //
  33. //    The following compilers are supported:
  34. //        MSC 6.0A
  35. //        MSC/C++ 7.0
  36. //        Borland C++ 3.1 for DOS
  37. //        Borland C++ 1.0 for OS/2 2.X
  38. //
  39. //----------------------------------------------------------------------------
  40. #include <z4.h>
  41.  
  42.  
  43. //----------------------------------------------------------------------------
  44. //    Globals
  45. //----------------------------------------------------------------------------
  46. SIZET Z4_PARSE::cBuf;
  47. CHAR Z4_PARSE::szBuffer[256];                        
  48. SIZET Z4_PARSE::cStack;                                
  49. Z4_PARSE_ELEM Z4_PARSE::pelem[MAX_PARSE_ELEM];
  50.  
  51.  
  52. //----------------------------------------------------------------------------
  53. //   Description:    Default constructor
  54. //    Parameters:
  55. //       Returns:    
  56. //----------------------------------------------------------------------------
  57. FN_M Z4_PARSE::Z4_PARSE()
  58. {
  59.     Z4_PARSE::Initialize(CL_INIT_CLASS);
  60. }
  61.  
  62.  
  63. //----------------------------------------------------------------------------
  64. //   Description:    Copy constructor
  65. //    Parameters:    rcz4_parse        Reference to object to copy.
  66. //       Returns:    
  67. //----------------------------------------------------------------------------
  68. FN_M Z4_PARSE::Z4_PARSE(RCZ4_PARSE rcz4_parse)
  69. {
  70.     Z4_PARSE::Initialize(CL_INIT_CLASS);
  71.     *this = rcz4_parse;
  72. }
  73.  
  74.  
  75. //----------------------------------------------------------------------------
  76. //   Description:    Destructor
  77. //    Parameters:
  78. //       Returns:    
  79. //----------------------------------------------------------------------------
  80. FN_M Z4_PARSE::~Z4_PARSE()
  81. {
  82.     Z4_PARSE::Destroy(FALSE);
  83. }
  84.  
  85.  
  86. //----------------------------------------------------------------------------
  87. //   Description:
  88. //    Parameters:
  89. //       Returns:    TRUE if successful.
  90. //----------------------------------------------------------------------------
  91. VOID FN_M Z4_PARSE::Abbreviate()
  92. {
  93.     Z4_AB_FILE ab_file;
  94.  
  95.     //
  96.     //    Abbreviate words
  97.     //
  98.     for (SIZET i = 0; i < cStack; ++i)
  99.         {
  100.         if ((pelem[i].fs & (Z4_P_NUMERIC|Z4_P_EOW)) == Z4_P_NUMERIC
  101.         && (pelem[i+1].fs & (Z4_P_ALPHA|Z4_P_EOW)) == (Z4_P_ALPHA|Z4_P_EOW)
  102.         && pelem[i+1].cLen)
  103.             {
  104.             PCSZ pcszWord = Word(pelem[i+1].cWord);
  105.  
  106.             if (strcmp(pcszWord, "ST") == 0
  107.             || strcmp(pcszWord, "RD") == 0
  108.             || strcmp(pcszWord, "TH") == 0
  109.             || strcmp(pcszWord, "ND") == 0)
  110.                 {
  111.                 pelem[i+1].fs |= Z4_P_ORDINAL;
  112.                 i++;
  113.                 continue;
  114.                 }
  115.             }
  116.         PCSZ pcszWord = Word(pelem[i].cWord);
  117.         BOOL fEOW = (pelem[i].fs & Z4_P_EOW) != 0;
  118.         if (ab_file.Find(pcszWord))
  119.             {
  120.             PCSZ pcszChg = ab_file.Abbreviation();
  121.             pelem[i].fs = ab_file.Flags() | (fEOW ? Z4_P_EOW: 0);
  122.             if (strcmp(pcszWord, pcszChg) != 0)
  123.                 {
  124.                 pelem[i].fs |= Z4_P_CHG;
  125.                 pelem[i].cWord = AddString(pcszChg);
  126.                 pelem[i].cLen = strlen(pcszChg);
  127.                 }
  128.             }
  129.         }
  130.     return ;
  131. }
  132.  
  133. //----------------------------------------------------------------------------
  134. //   Description:    
  135. //    Parameters:
  136. //       Returns:    TRUE if successful.
  137. //----------------------------------------------------------------------------
  138. SIZET FN_M Z4_PARSE::AddString(PCCHAR pch, SIZET cch)
  139. {
  140.     if (!cch)
  141.         cch = strlen(pch);
  142.  
  143.     Assert(sizeof(szBuffer) - cBuf > cch + 1);
  144.  
  145.     SIZET cOffset = cBuf;
  146.     memcpy(szBuffer + cBuf, pch, cch);
  147.     cBuf += cch;
  148.     szBuffer[cBuf] = '\0';
  149.     cBuf++;
  150.     return cOffset;
  151. }
  152.  
  153.  
  154. //----------------------------------------------------------------------------
  155. //   Description: Handle specal cases where a single letter follows the word
  156. //                        'AVENUE'.
  157. //    
  158. //    AVENUE N                  ->         AVENUE N 
  159. //    N E AVENUE N            ->    NE   AVENUE N 
  160. //    N E AVENUE N W            ->    NE   AVENUE N W        
  161. //    N E AVENUE N N W        ->    NE   AVENUE N NW
  162. //    N E AVENUE N NW        ->    NE   AVENUE N NW
  163. //
  164. //    Parameters:
  165. //       Returns:    TRUE if successful.
  166. //----------------------------------------------------------------------------
  167. VOID FN_M Z4_PARSE::AvenueX()
  168. {
  169.     SIZET i = 0, j;
  170.                                                     // Predirection
  171.     if (i < cStack && BTEST(pelem[i].fs, Z4_P_DIR))
  172.         {
  173.         PCSZ pcsz1 = Word(pelem[i].cWord);
  174.         SIZET cLen = pelem[i].cLen;
  175.         i++;
  176.  
  177.         if (cLen == 1
  178.         && (pcsz1[0] == 'N'
  179.         ||    pcsz1[0] == 'S')
  180.         && i < cStack
  181.         && BTEST(pelem[i].fs, Z4_P_DIR)
  182.         && pelem[i].cLen == 1)
  183.             {
  184.             PCSZ pcsz2 = Word(pelem[i].cWord);
  185.  
  186.             if (pcsz2[0] == 'W' || pcsz2[0] == 'E')
  187.                 i++;
  188.             }
  189.         }
  190.                                                     // Avenue X
  191.     if (i < cStack
  192.     && BTEST(pelem[i].fs, Z4_P_SUFFIX)
  193.     && strcmp(Word(pelem[i].cWord), "AVE") == 0)
  194.         {
  195.         j = i;
  196.         i++;
  197.         if (i < cStack
  198.         && strlen(Word(pelem[i].cFull)) == 1
  199.         && BTEST(pelem[i].fs, Z4_P_EOW))
  200.             i++;
  201.         else
  202.             return ;
  203.         }
  204.     else
  205.         return ;
  206.                                                     // Post direction
  207.     if (i < cStack && BTEST(pelem[i].fs, Z4_P_DIR))
  208.         {
  209.         PCSZ pcsz1 = Word(pelem[i].cWord);
  210.         SIZET cLen = pelem[i].cLen;
  211.         i++;
  212.  
  213.         if (cLen == 1
  214.         && (pcsz1[0] == 'N'
  215.         ||    pcsz1[0] == 'S')
  216.         && i < cStack
  217.         && BTEST(pelem[i].fs, Z4_P_DIR)
  218.         && pelem[i].cLen == 1)
  219.             {
  220.             PCSZ pcsz2 = Word(pelem[i].cWord);
  221.  
  222.             if (pcsz2[0] == 'W' || pcsz2[0] == 'E')
  223.                 i++;
  224.             }
  225.         }
  226.     if (i < cStack)                            // Not at the end of the address,
  227.         return ;                                    //  therefore not of correct form
  228.  
  229.     CHAR szWord[20];
  230.     sprintf(szWord, "AVENUE %s", Word(pelem[j + 1].cFull));
  231.  
  232.     pelem[j].fs = Z4_P_ALPHA|Z4_P_CHG|Z4_P_EOW;
  233.     pelem[j].cWord = AddString(szWord);
  234.     pelem[j].cFull = AddString(szWord);
  235.     pelem[j].cLen = strlen(szWord);
  236.     Delete(j + 1, 1);
  237.     return ;
  238. }
  239.  
  240.  
  241. //----------------------------------------------------------------------------
  242. //   Description:    
  243. //    Parameters:
  244. //       Returns:    TRUE if successful.
  245. //----------------------------------------------------------------------------
  246. VOID FN_M Z4_PARSE::Break(PCSZ pcsz)
  247. {
  248.     cBuf = 0;                                    // Clear word buffer
  249.     cStack = 0;                                    // Clear stack
  250.     memset(pelem, 0, sizeof(pelem));
  251.     PCHAR pchBegin = (PCHAR)pcsz;
  252.     while (pchBegin[0])
  253.         {
  254.         PCHAR pch = pchBegin;
  255.         FLAG16 fs;
  256.  
  257.         if (isascii(pch[0]) && isalpha(pch[0]))
  258.             {
  259.             pch++;                                // Alpha
  260.             while (isascii(pch[0]) && isalpha(pch[0]))
  261.                 pch++;
  262.             fs = Z4_P_ALPHA;
  263.             }
  264.         else if (isascii(pch[0]) && isdigit(pch[0]))
  265.             {
  266.             pch++;                                // Numeric
  267.             while (isascii(pch[0]) && isdigit(pch[0]))
  268.                 pch++;
  269.             fs = Z4_P_NUMERIC;
  270.             }
  271.         else if (isascii(pch[0]) && isspace(pch[0]))
  272.             {
  273.             pch++;                                // Spaces -- skip over them!
  274.             while (isascii(pch[0]) && isspace(pch[0]))
  275.                 pch++;
  276.  
  277.             if (cStack)                            // Add end of word marker
  278.                 pelem[cStack - 1].fs |= Z4_P_EOW;
  279.  
  280.             pchBegin = pch;                    // Don't add anything to stack!
  281.             }
  282.         else
  283.             {
  284.             pch++;                                // Only add on character to stack
  285.             fs = Z4_P_OTHER;
  286.             }
  287.         SIZET cLen = (SIZET)(pch - pchBegin);
  288.         if (cLen)                                // Add phrase to stack
  289.             {
  290.             SIZET cWord = AddString(pchBegin, cLen);
  291.             Assert(cStack < MAX_PARSE_ELEM);
  292.             pelem[cStack].fs = fs;
  293.             pelem[cStack].cLen = cLen;
  294.             pelem[cStack].cWord = cWord;
  295.             pelem[cStack].cFull = cWord;
  296.             cStack++;
  297.             }
  298.         pchBegin = pch;
  299.         }
  300.     if (cStack)                                    // Place end of word marker on last word
  301.         pelem[cStack - 1].fs |= Z4_P_EOW;
  302.     return ;
  303. }
  304.  
  305.  
  306. //----------------------------------------------------------------------------
  307. //   Description:
  308. //    Parameters:
  309. //       Returns:    TRUE if successful.
  310. //----------------------------------------------------------------------------
  311. BOOL FN_M Z4_PARSE::City()
  312. {
  313.     Combine(szCity, sizeof(szCity));
  314.     return TRUE;
  315. }
  316.  
  317.  
  318. //----------------------------------------------------------------------------
  319. //   Description:    
  320. //    Parameters:
  321. //       Returns:    TRUE if successful.
  322. //----------------------------------------------------------------------------
  323. VOID FN_M Z4_PARSE::Clean()
  324. {
  325.     for (SIZET i = 0; i < cStack; ++i)
  326.         if (pelem[i].fs & Z4_P_OTHER)
  327.             {
  328.             PCSZ pcszWord = Word(pelem[i].cWord);
  329.             if (pcszWord[0] == '.' && !pcszWord[1])
  330.                 {
  331.                 if (i
  332.                 && !BTEST(pelem[i-1].fs, Z4_P_EOW)
  333.                 && BTEST(pelem[i].fs, Z4_P_EOW))
  334.                     pelem[i-1].fs |= Z4_P_EOW;
  335.  
  336.                 Delete(i);                        // Delete the period!
  337.                 i--;                                // Remain at current word!
  338.                 }
  339.             }
  340.     return ;
  341. }
  342.  
  343.  
  344. //----------------------------------------------------------------------------
  345. //   Description:    
  346. //    Parameters:
  347. //       Returns:    TRUE if successful.
  348. //----------------------------------------------------------------------------
  349. VOID FN_M Z4_PARSE::Clear()
  350. {
  351.     cBuf = 0;                                    // Clear word buffer
  352.     cStack = 0;                                    // Clear stack
  353.     memset(pelem, 0, sizeof(pelem));
  354.  
  355.     szAddr1[0] = '\0';                        // Input areas
  356.     szAddr2[0] = '\0';
  357.     szLastLine[0] = '\0';
  358.  
  359.     szCity[0] = '\0';                            // Last line
  360.     state = Z4_ST_INVALID;
  361.     fInvState = FALSE;
  362.     szZip5[0] = '\0';
  363.     szAddon[0] = '\0';
  364.     szZip4[0] = '\0';
  365.  
  366.     szPriNo[0] = '\0';                        // Address line 2
  367.     szPriNoReformat[0] = '\0';
  368.     predir = Z4_DIR_BLANK;
  369.     szPriName[0] = '\0';
  370.     fPriNumeric = FALSE;
  371.     suffix1 = 0;
  372.     suffix2 = 0;
  373.     postdir = Z4_DIR_BLANK;
  374.     unit = Z4_UNIT_BLANK;
  375.     szSecNo[0] = '\0';
  376.  
  377.     szSecName[0] = '\0';                        // Address line 1
  378.     return ;
  379. }
  380.  
  381.  
  382. //----------------------------------------------------------------------------
  383. //   Description:    
  384. //    Parameters:
  385. //       Returns:    TRUE if successful.
  386. //----------------------------------------------------------------------------
  387. VOID FN_M Z4_PARSE::Collapse()
  388. {
  389. #define MAX_CWORD        (5)
  390.  
  391. struct Z4_PARSE_COLLAPSE
  392.     {
  393.     PCSZ pcsz;
  394.     FLAG16 fs;
  395.     PCSZ apcsz[MAX_CWORD];
  396.     };
  397. static Z4_PARSE_COLLAPSE _FAR_ collapse[] =
  398.     {
  399.     { "RR",                Z4_P_RR|Z4_P_ALPHA,        { "R", "RT" }},
  400.     { "RR",                Z4_P_RR|Z4_P_ALPHA,        { "R", ".", "RT" }},
  401.     { "RR",                Z4_P_RR|Z4_P_ALPHA,        { "R", "R" }},
  402.     { "RR",                Z4_P_RR|Z4_P_ALPHA,        { "R", ".", "R", "."}},
  403.     { "RR",                Z4_P_RR|Z4_P_ALPHA,        { "RURAL", "RR" }},
  404.     { "POB",                Z4_P_POB|Z4_P_ALPHA,     { "PO", "BOX" }},
  405.     { "PO",                Z4_P_ALPHA,                 { "POST", "OFC" }},
  406.     { "HC",                Z4_P_HC|Z4_P_ALPHA,         { "HWY", "CONTRACT" }},
  407.     { NULL }
  408.     };
  409.  
  410.     for (SIZET i = 0; i < cStack; ++i)
  411.         {
  412.         for (SHORT j = 0; collapse[j].pcsz; ++j)
  413.             {
  414.             BOOL fMatch = TRUE;
  415.             CHAR szFull[MAX_ADDR+1];
  416.             szFull[0] = '\0';
  417.             for (SIZET k = 0; k < MAX_CWORD && collapse[j].apcsz[k]; ++k)
  418.                 {
  419.                 PCSZ pcszWord = Word(pelem[i+k].cWord);
  420.                 if (i + k >= cStack
  421.                 || strcmp(pcszWord, collapse[j].apcsz[k]) != 0)
  422.                     {
  423.                     fMatch = FALSE;
  424.                     break;
  425.                     }
  426.                 strcats(szFull, pcszWord);
  427.                 }
  428.             if (fMatch)                            // Collapse 2 or more words into one
  429.                 {
  430.                 Assert(k > 1);
  431.                 pelem[i].fs = collapse[j].fs|Z4_P_CHG|Z4_P_EOW;
  432.                 pelem[i].cWord = AddString(collapse[j].pcsz);
  433.                 pelem[i].cFull = AddString(szFull);
  434.                 pelem[i].cLen = strlen(collapse[j].pcsz);
  435.                 Delete(i + 1, k - 1);
  436.                 i--;                                // Restart at current word!
  437.                 break;
  438.                 }
  439.             }
  440.         }
  441.     return ;
  442. }
  443.  
  444.  
  445. //----------------------------------------------------------------------------
  446. //   Description:    
  447. //    Parameters:
  448. //       Returns:    TRUE if successful.
  449. //----------------------------------------------------------------------------
  450. PCSZ FN_M Z4_PARSE::Combine(PSZ psz, SIZET cch, SIZET cStart, SIZET cEnd, BOOL fSpace)
  451. {
  452.     PSZ pszStart = psz;
  453.  
  454.     cEnd = MIN(cEnd, cStack);
  455.     cStart = MIN(cStart, cEnd);
  456.  
  457.     Assert(cch >= 1);
  458.     cch--;                                        // Leave room for null terminator
  459.     for (SIZET i = cStart; cch && i < cEnd; ++i)
  460.         {
  461.         PSZ pszWord = Word(pelem[i].cFull);
  462.         SIZET cLen = strlen(pszWord);
  463.         if (cch < cLen)                        // If no room for word, break
  464.             break;
  465.                                                     // Concat word to string
  466.         memcpy(psz, pszWord, cLen);        
  467.         psz += cLen;
  468.         cch -= cLen;
  469.                                                     // Add space after a word
  470.         if (cch && (pelem[i].fs & Z4_P_EOW) && fSpace)
  471.             {
  472.             *psz++ = ' ';
  473.             cch--;
  474.             }
  475.         }
  476.     psz[0] = '\0';                                // Null terminator
  477.     strtrimright(pszStart);                    // Remove trailing spaces
  478.     return pszStart;
  479. }
  480.  
  481.  
  482. //----------------------------------------------------------------------------
  483. //   Description:
  484. //    Parameters:
  485. //                        cElem        Number of elements to delete
  486. //                                    Default is 1.
  487. //       Returns:    TRUE if successful.
  488. //----------------------------------------------------------------------------
  489. VOID FN_M Z4_PARSE::Delete(SIZET cDelete, SIZET cElem)
  490. {
  491.     Assert(cStack);
  492.     Assert(cDelete < cStack);
  493.  
  494.     cElem = MIN(MAX(1, cElem), cStack - cDelete);
  495.     SIZET cEnd = cDelete + cElem;            // Element at end of delete area
  496.     SIZET cMove = cStack - cEnd;            // Element to move down
  497.  
  498.     if (cMove)
  499.         memcpy(pelem + cDelete, pelem + cEnd, cMove * sizeof(Z4_PARSE_ELEM));
  500.  
  501.     cStack -= cElem;
  502.     return ;
  503. }
  504.  
  505.  
  506. //----------------------------------------------------------------------------
  507. //   Description:    Destroy object. Free any resources used by object.
  508. //                          Normally called by destructor.
  509. //                        Should allow multiple calls from various classes.
  510. //                        A class should almost always re-init its variables when 
  511. //                        it is destroyed to prevent accidents.
  512. //    Parameters:    fDestroyAll        Destroy parents also?
  513. //                                            Default is TRUE.
  514. //       Returns:    TRUE if successful.
  515. //----------------------------------------------------------------------------
  516. BOOL FN_M Z4_PARSE::Destroy(BOOL fDestroyAll)
  517. {
  518.     Z4_PARSE::Initialize(CL_INIT_CLASS_VARS);
  519.     if (fDestroyAll)                            // Destroy parent.
  520.         Z4_PARSE_PARENT::Destroy(fDestroyAll);
  521.     return TRUE;
  522. }
  523.  
  524.  
  525. //----------------------------------------------------------------------------
  526. //   Description:    Initialize object. 
  527. //                          Normally called by constructor.
  528. //                        Should allow multiple calls from various classes.
  529. //    Parameters:    sInit        Initialization code. May be one of the following:
  530. //                                        CL_INIT_CLASS            Reset class variables and
  531. //                                                                    and dynamic allocations for
  532. //                                                                    this class only.
  533. //                                        CL_INIT_CLASS_VARS    Reset class variables for
  534. //                                                                    this class only.
  535. //                                        CL_INIT_VARS            Reset class variables for
  536. //                                                                    this class only.
  537. //                                        CL_INIT_ALL                Initialize class and all 
  538. //                                                                    parent class, including
  539. //                                                                    dynamic memory allocation.
  540. //                                    Default is CL_INIT_ALL
  541. //       Returns:    TRUE if successful.
  542. //----------------------------------------------------------------------------
  543. BOOL FN_M Z4_PARSE::Initialize(SHORT sInit)
  544. {
  545.     if (sInit == CL_INIT_VARS || sInit == CL_INIT_ALL)
  546.         Z4_PARSE_PARENT::Initialize(sInit);
  547.  
  548.     Clear();
  549.     return TRUE;
  550. }
  551.  
  552.  
  553. //----------------------------------------------------------------------------
  554. //   Description:    
  555. //    Parameters:
  556. //       Returns:    TRUE if successful.
  557. //----------------------------------------------------------------------------
  558. BOOL FN_M Z4_PARSE::LastLine(PCSZ pcszZip, PCSZ pcszCity, PCSZ pcszState)
  559. {
  560.     if (!pcszCity)
  561.         pcszCity = "";
  562.     if (!pcszState)
  563.         pcszState = "";
  564.     if (!pcszZip)
  565.         pcszZip = "";
  566.  
  567.     //
  568.     //    Build the complete last line
  569.     //
  570.     strnzcpy(szLastLine, pcszCity, sizeof(szLastLine));
  571.     if (strlen(szLastLine) + strlen(pcszState) + 1 < sizeof(szLastLine))
  572.         strcats(szLastLine, pcszState);
  573.     if (strlen(szLastLine) + strlen(pcszZip) + 1 < sizeof(szLastLine))
  574.         strcats(szLastLine, pcszZip);
  575.     strupr(szLastLine);
  576.  
  577.     //
  578.     //    Parse the last line
  579.     //
  580.     Break(szLastLine);
  581.     Clean();
  582.     Zip4();
  583.     State();
  584.     City();
  585.     return TRUE;
  586. }
  587.  
  588.  
  589. //----------------------------------------------------------------------------
  590. //   Description:    
  591. //    Parameters:
  592. //       Returns:    TRUE if successful.
  593. //----------------------------------------------------------------------------
  594. VOID FN_M Z4_PARSE::Ordinal()
  595. {
  596.     //
  597.     //    Delete any ordinals which follow numerics. IE 10TH --> 10
  598.     //
  599.     for (SIZET i = 0; i + 1 < cStack; ++i)
  600.         {
  601.         if ((pelem[i].fs & (Z4_P_NUMERIC|Z4_P_EOW)) == Z4_P_NUMERIC
  602.         && (pelem[i+1].fs & (Z4_P_ALPHA|Z4_P_EOW)) == (Z4_P_ALPHA|Z4_P_EOW)
  603.         && pelem[i+1].cLen)
  604.             {
  605.             PCSZ pcszWord = Word(pelem[i+1].cWord);
  606.  
  607.             if (strcmp(pcszWord, "ST") == 0
  608.             || strcmp(pcszWord, "RD") == 0
  609.             || strcmp(pcszWord, "TH") == 0
  610.             || strcmp(pcszWord, "ND") == 0)
  611.                 {
  612.                 Delete(i + 1, 1);
  613.                 pelem[i].fs |= Z4_P_EOW|Z4_P_ORDINAL;
  614.                 }
  615.             }
  616.         }
  617.     return ;
  618. }
  619.  
  620.  
  621. //----------------------------------------------------------------------------
  622. //   Description:    
  623. //    Parameters:
  624. //       Returns:    TRUE if successful.
  625. //----------------------------------------------------------------------------
  626. BOOL FN_M Z4_PARSE::POB()
  627. {
  628.     if (pelem[0].fs & Z4_P_POB)
  629.         {
  630.         szSecNo[0] = '\0';                    // No secondary number
  631.         strcpy(szPriName, "PO BOX");
  632.         Combine(szPriNo, sizeof(szPriNo), 1);
  633.         strcpy(szPriNoReformat, szPriNo);
  634.         return TRUE;
  635.         }
  636.     return FALSE;
  637. }
  638.  
  639.  
  640. //----------------------------------------------------------------------------
  641. //   Description:    
  642. //    Parameters:
  643. //       Returns:    TRUE if successful.
  644. //----------------------------------------------------------------------------
  645. VOID FN_M Z4_PARSE::PostDir()
  646. {
  647.     if (cStack > 2
  648.     && (pelem[cStack - 1].fs & Z4_P_DIR)
  649.     && (pelem[cStack - 2].fs & Z4_P_DIR))
  650.         {
  651.         PCSZ pcsz1 = Word(pelem[cStack - 2].cWord);
  652.         PCSZ pcsz2 = Word(pelem[cStack - 1].cWord);
  653.  
  654.         if (pcsz1[0] == 'N')
  655.             {
  656.             if (pcsz2[0] == 'E')
  657.                 postdir = Z4_DIR_NE;
  658.             else if (pcsz2[0] == 'W')
  659.                 postdir = Z4_DIR_NW;
  660.             }
  661.         else if (pcsz1[0] == 'S')
  662.             {
  663.             if (pcsz2[0] == 'E')
  664.                 postdir = Z4_DIR_SE;
  665.             else if (pcsz2[0] == 'W')
  666.                 postdir = Z4_DIR_SW;
  667.             }
  668.         if (postdir != Z4_DIR_BLANK)
  669.             Delete(cStack - 2, 2);
  670.         }
  671.     if (cStack > 1
  672.     && (pelem[cStack - 1].fs & Z4_P_DIR))
  673.         {
  674.         PCSZ pcsz = Word(pelem[cStack - 1].cWord);
  675.         switch (pcsz[0])
  676.             {
  677.             case 'N':
  678.                 if (pcsz[1] == 'W')
  679.                     postdir = Z4_DIR_NW;
  680.                 else if (pcsz[1] == 'E')
  681.                     postdir = Z4_DIR_NE;
  682.                 else
  683.                     postdir = Z4_DIR_N;
  684.                 break;
  685.  
  686.             case 'S':
  687.                 if (pcsz[1] == 'W')
  688.                     postdir = Z4_DIR_SW;
  689.                 else if (pcsz[1] == 'E')
  690.                     postdir = Z4_DIR_SE;
  691.                 else
  692.                     postdir = Z4_DIR_S;
  693.                 break;
  694.  
  695.             case 'E':
  696.                 postdir = Z4_DIR_E;
  697.                 break;
  698.  
  699.             case 'W':
  700.                 postdir = Z4_DIR_W;
  701.                 break;
  702.  
  703.             default:
  704.                 return ;
  705.             }
  706.         Delete(cStack - 1, 1);
  707.         }
  708.     return ;
  709. }
  710.  
  711.  
  712. //----------------------------------------------------------------------------
  713. //   Description:
  714. //    Parameters:
  715. //       Returns:    TRUE if successful.
  716. //----------------------------------------------------------------------------
  717. VOID FN_M Z4_PARSE::PreDir()
  718. {
  719.     if (cStack > 2
  720.     && (pelem[0].fs & Z4_P_DIR)
  721.     && (pelem[1].fs & Z4_P_DIR))
  722.         {
  723.         PCSZ pcsz1 = Word(pelem[0].cWord);
  724.         PCSZ pcsz2 = Word(pelem[1].cWord);
  725.  
  726.         if (pcsz1[0] == 'N')
  727.             {
  728.             if (pcsz2[0] == 'E')
  729.                 predir = Z4_DIR_NE;
  730.             else if (pcsz2[0] == 'W')
  731.                 predir = Z4_DIR_NW;
  732.             }
  733.         else if (pcsz1[0] == 'S')
  734.             {
  735.             if (pcsz2[0] == 'E')
  736.                 predir = Z4_DIR_SE;
  737.             else if (pcsz2[0] == 'W')
  738.                 predir = Z4_DIR_SW;
  739.             }
  740.         if (predir != Z4_DIR_BLANK)
  741.             Delete(0, 2);
  742.         }
  743.     if (cStack > 1
  744.     && (pelem[0].fs & Z4_P_DIR))
  745.         {
  746.         PCSZ pcsz = Word(pelem[0].cWord);
  747.         switch (pcsz[0])
  748.             {
  749.             case 'N':
  750.                 if (pcsz[1] == 'W')
  751.                     predir = Z4_DIR_NW;
  752.                 else if (pcsz[1] == 'E')
  753.                     predir = Z4_DIR_NE;
  754.                 else
  755.                     predir = Z4_DIR_N;
  756.                 break;
  757.  
  758.             case 'S':
  759.                 if (pcsz[1] == 'W')
  760.                     predir = Z4_DIR_SW;
  761.                 else if (pcsz[1] == 'E')
  762.                     predir = Z4_DIR_SE;
  763.                 else
  764.                     predir = Z4_DIR_S;
  765.                 break;
  766.  
  767.             case 'E':
  768.                 predir = Z4_DIR_E;
  769.                 break;
  770.  
  771.             case 'W':
  772.                 predir = Z4_DIR_W;
  773.                 break;
  774.  
  775.             default:
  776.                 return ;
  777.             }
  778.         Delete(0,1);
  779.         }
  780.     return ;
  781. }
  782.  
  783.  
  784. //----------------------------------------------------------------------------
  785. //   Description:    
  786. //    Parameters:
  787. //       Returns:    TRUE if successful.
  788. //----------------------------------------------------------------------------
  789. BOOL FN_M Z4_PARSE::Primary(PCSZ pcszAddr2)
  790. {
  791.     Assert(pcszAddr2);
  792.     strnzcpy(szAddr2, pcszAddr2, sizeof(szAddr2));
  793.     strupr(szAddr2);
  794.  
  795.     Break(szAddr2);
  796.     Abbreviate();
  797.     AvenueX();
  798.     Collapse();
  799.     SecNo();
  800.     if (RRHC())
  801.         return TRUE;
  802.     if (POB())
  803.         return TRUE;
  804.     PriNo();
  805.     Ordinal();
  806.     Clean();
  807.     Collapse();
  808.     PostDir();
  809.     suffix1 = Suffix();
  810.     suffix2 = Suffix();
  811.     if (suffix2 == 0)
  812.         {
  813.         suffix2 = suffix1;
  814.         suffix1 = 0;
  815.         }
  816.     PreDir();
  817.     PriName();
  818.  
  819.     if (strlen(szSecNo) > MAX_SEC_NO)
  820.         szSecNo[MAX_SEC_NO] = '\0';
  821.     if (strlen(szPriNo) > MAX_PRI_NO)
  822.         szPriNo[MAX_PRI_NO] = '\0';
  823.     if (strlen(szPriNoReformat) > MAX_PRI_NO)
  824.         szPriNoReformat[MAX_PRI_NO] = '\0';
  825.  
  826.     return TRUE;
  827. }
  828.  
  829.  
  830. //----------------------------------------------------------------------------
  831. //   Description:    
  832. //    Parameters:
  833. //       Returns:    TRUE if successful.
  834. //----------------------------------------------------------------------------
  835. VOID FN_M Z4_PARSE::PriName()
  836. {
  837.     if (cStack == 1 && (pelem[0].fs & Z4_P_NUMERIC))
  838.         strcpy(szPriName,Word(pelem[0].cWord));
  839.     else
  840.         Combine(szPriName, sizeof(szPriName));
  841.  
  842.     fPriNumeric = TRUE;
  843.     for (SIZET i = 0; szPriName[i]; ++i)
  844.         if (!isdigit(szPriName[i]))
  845.             fPriNumeric = FALSE;
  846.  
  847.     return ;
  848. }
  849.  
  850.  
  851. //----------------------------------------------------------------------------
  852. //   Description:    
  853. //    Parameters:
  854. //       Returns:    TRUE if successful.
  855. //----------------------------------------------------------------------------
  856. VOID FN_M Z4_PARSE::PriNo()
  857. {
  858.     Range(szPriNo, sizeof(szPriNo), 0, Z4_P_PRINO);
  859.     strcpy(szPriNoReformat, szPriNo);
  860.     return ;
  861. }
  862.  
  863.  
  864. //----------------------------------------------------------------------------
  865. //   Description:    
  866. //    Parameters:
  867. //       Returns:    TRUE if successful.
  868. //----------------------------------------------------------------------------
  869. BOOL FN_M Z4_PARSE::Range(PSZ psz, SIZET cch, SIZET cStart, FLAG16 fs)
  870. {
  871.     SIZET cTotal = 0;                            // Size of number
  872.     BOOL fDigit = FALSE;                        // Last digit numeric?
  873.     BOOL fNumeric = FALSE;                    // Numeric characters found?
  874.     BOOL fPrimary = BTEST(fs, Z4_P_PRINO);
  875.  
  876.     //
  877.     //    Do not extract single word secondary ranges if they
  878.     //    are also a directional or suffix word.
  879.     //    Ignore this rule if 
  880.     //    EX: BAY MEADOWS TRAILER CT
  881.     //
  882.     if (!fPrimary
  883.     && BTEST(pelem[cStart].fs, Z4_P_EOW)
  884.     && BTEST(pelem[cStart].fs, Z4_P_SUFFIX|Z4_P_DIR)
  885.     && !BTEST(fs, Z4_P_SECNO_FORCE))
  886.         return FALSE;
  887.         
  888.     //
  889.     //    Do not extract primary ranges the word is an ordinal number.
  890.     //    This parses 13TH ST as having no range.
  891.     //
  892.     //
  893.     if (fPrimary
  894.     && (pelem[cStart].fs & Z4_P_NUMERIC)
  895.     && (pelem[cStart+1].fs & Z4_P_ORDINAL))
  896.         return FALSE;
  897.  
  898.     for (SIZET i = cStart; i < cStack; ++i)
  899.         {
  900.         PCSZ pcsz = Word(pelem[i].cWord);
  901.         if (pelem[i].fs & Z4_P_NUMERIC)    // Word is numeric
  902.             {
  903.             cTotal += pelem[i].cLen;
  904.             fDigit = TRUE;
  905.             fNumeric = TRUE;
  906.             }                                        // Word is alpha
  907.         else if (pelem[i].fs & Z4_P_ALPHA)
  908.             {
  909.             cTotal += pelem[i].cLen;
  910.             fDigit = FALSE;
  911.             }                                        // Either a dash or period
  912.         else if ((pelem[i].fs & Z4_P_OTHER)
  913.         && pelem[i].cLen == 1
  914.         && (pcsz[0] == '-' || pcsz[0] == '.'))
  915.             {
  916.             cTotal++;
  917.             fDigit = FALSE;
  918.             }
  919.         else
  920.             break;
  921.  
  922.         if (pelem[i].fs & Z4_P_EOW)        // End of word, break
  923.             {
  924.             i++;
  925.             break;
  926.             }
  927.         }
  928.     if (i == cStart)                            // Nothing found!
  929.         return FALSE;
  930.  
  931.     if (!fNumeric && fPrimary)                // No numeric found, so exit
  932.         return FALSE;
  933.  
  934.     SIZET cFract = i, cFractTotal = 0;
  935.  
  936.     if (cFract < cStack)                        // Single numeric (as in 1 / 2)
  937.         {
  938.         if ((pelem[cFract].fs & Z4_P_NUMERIC)
  939.         && pelem[cFract].cLen == 1)
  940.             {
  941.               fDigit = TRUE;
  942.             cFractTotal++;
  943.             cFract++;
  944.             }
  945.         }
  946.     if (fDigit                                    // First character of fraction found,
  947.     && cFract < cStack                        // look for remainder
  948.     && (pelem[cFract].fs & Z4_P_OTHER)
  949.     && pelem[cFract].cLen == 1)
  950.         {
  951.         PCSZ pcsz = Word(pelem[cFract].cWord);
  952.         if (pcsz[0] == '/')                    // Look for denominator
  953.             {
  954.             cFract++;
  955.             cFractTotal++;
  956.             if (cFract < cStack
  957.             && (pelem[cFract].fs & Z4_P_NUMERIC)
  958.             && pelem[cFract].cLen == 1)
  959.                 {
  960.                 cFractTotal++;
  961.                 i = cFract + 1;
  962.                 }
  963.             }
  964.         }
  965.     if (cTotal + cFractTotal >= cch)        // Invalid number, too long
  966.         return FALSE;
  967.  
  968.     if (i >= cStack && cStart == 0)        // Don't extract primary number if nothing 
  969.         return FALSE;                            // would remain
  970.                                                     // Extract and remove from stack
  971.     Combine(psz, cch, cStart, i, FALSE);
  972.     Delete(cStart, i - cStart);
  973.     return TRUE;
  974. }
  975.  
  976.  
  977. //----------------------------------------------------------------------------
  978. //   Description:    
  979. //    Parameters:
  980. //       Returns:    TRUE if successful.
  981. //----------------------------------------------------------------------------
  982. BOOL FN_M Z4_PARSE::RRHC()
  983. {
  984.     if (pelem[0].fs & (Z4_P_RR|Z4_P_HC))
  985.         {
  986.         strcpy(szPriNo, szSecNo);            // Move secondary number, if any, to the
  987.         szSecNo[0] = '\0';                    // primary number
  988.         strcpy(szPriNoReformat, szPriNo);
  989.  
  990.  
  991.         if (pelem[0].fs & Z4_P_RR)
  992.             strcpy(szPriName, "RR ");
  993.         else
  994.             strcpy(szPriName, "HC ");
  995.  
  996.         Combine(strchr(szPriName, '\0'), sizeof(szPriName), 1);
  997.         return TRUE;
  998.         }
  999.     return FALSE;
  1000. }
  1001.  
  1002.  
  1003. //----------------------------------------------------------------------------
  1004. //   Description:    
  1005. //    Parameters:
  1006. //       Returns:    TRUE if successful.
  1007. //----------------------------------------------------------------------------
  1008. BOOL FN_M Z4_PARSE::Secondary(PCSZ pcszAddr1)
  1009. {
  1010.     Assert(pcszAddr1);
  1011.     strnzcpy(szAddr1, pcszAddr1, sizeof(szAddr1));
  1012.     Z4Clean(szAddr1);
  1013.  
  1014.     //
  1015.     //    Temporary address line 1 parse. Does not look for
  1016.     //    anything special like secondary ranges.
  1017.     //
  1018.     strnzcpy(szSecName, szAddr1, sizeof(szSecName));
  1019.     return TRUE;
  1020. }
  1021.  
  1022.  
  1023. //----------------------------------------------------------------------------
  1024. //   Description:
  1025. //    Parameters:
  1026. //       Returns:    TRUE if successful.
  1027. //----------------------------------------------------------------------------
  1028. VOID FN_M Z4_PARSE::SecNo()
  1029. {
  1030.     if (cStack <= 1)
  1031.         return ;
  1032.  
  1033.     //
  1034.     //    Look for a normal unit designator, followed by a secondary number
  1035.     //
  1036.     if (cStack <= 1)
  1037.         return ;
  1038.  
  1039.     for (SIZET i = cStack - 2; i > 1; --i)
  1040.         if (pelem[i].fs & Z4_P_UNIT)
  1041.             {
  1042.             FLAG16 fs = Z4_P_SECNO;
  1043.  
  1044.             if (pelem[i].cLen == 1)            // '#'
  1045.                 fs |= Z4_P_SECNO_FORCE;
  1046.             if (Range(szSecNo, sizeof(szSecNo), i + 1, fs))
  1047.                 {
  1048.                PCSZ pcsz = Word(pelem[i].cWord);
  1049.  
  1050.                 unit = Z4FindUnit(pcsz);
  1051.                 if (unit != Z4_UNIT_BLANK)
  1052.                     {
  1053.                     Delete(i, 1);
  1054.                     return ;
  1055.                     }
  1056.                 szSecNo[0] = '\0';            // This should not happen!!!
  1057.                 }
  1058.             }
  1059.     //
  1060.     //    Normal unit designator not found, look for an exceptional
  1061.     //    unit designator
  1062.     //
  1063.     for (i = 1; i < cStack; ++i)
  1064.         if (pelem[i].fs & Z4_P_EXCEPT)
  1065.             {
  1066.             PCSZ pcsz = Word(pelem[i].cWord);
  1067.             unit = Z4FindUnit(pcsz);
  1068.             if (unit != Z4_UNIT_BLANK)
  1069.                 {
  1070.                 strcpy(szSecNo, pcsz);
  1071.                 Delete(i, 1);
  1072.                 return ;
  1073.                 }
  1074.             }
  1075.     return ;
  1076. }
  1077.  
  1078.  
  1079. //----------------------------------------------------------------------------
  1080. //   Description:    
  1081. //    Parameters:
  1082. //       Returns:    TRUE if successful.
  1083. //----------------------------------------------------------------------------
  1084. BOOL FN_M Z4_PARSE::State()
  1085. {
  1086.     while (cStack && BTEST(pelem[cStack - 1].fs, (Z4_P_NUMERIC|Z4_P_OTHER)))
  1087.         Delete(cStack - 1, 1);
  1088.     //
  1089.     //    Be careful of states such as Virginia vs West Virginia. Don't just
  1090.     //    extract the first word at the end of the line.
  1091.     //
  1092.     // First attempt is to pull a 2 digit state code off the end of the
  1093.     //    word.
  1094.     //
  1095.     if (cStack >= 1
  1096.     && (pelem[cStack - 1].fs & Z4_P_ALPHA)
  1097.     && pelem[cStack - 1].cLen == MAX_STATE)
  1098.         {
  1099.         CHAR szState[MAX_STATE+1];
  1100.  
  1101.         strcpy(szState, Word(pelem[cStack - 1].cWord));
  1102.         state = Z4_INQ::st_file.Find(szState);
  1103.         if (state == Z4_ST_INVALID)
  1104.             fInvState = TRUE;
  1105.  
  1106.         Delete(cStack - 1, 1);
  1107.         }
  1108.     else if (cStack >= 1)
  1109.         {
  1110.         CHAR szState[MAX_ADDR_LINE+1];
  1111.  
  1112.         for (SIZET i = 0; i < cStack; ++i)
  1113.             {
  1114.             Combine(szState, sizeof(szState), i);
  1115.             state = Z4_INQ::st_file.Find(szState);
  1116.             if (state != Z4_ST_INVALID)
  1117.                 {
  1118.                 Delete(i, 0xFFFF);
  1119.                 return TRUE;
  1120.                 }
  1121.             }
  1122.         }
  1123.     return TRUE;
  1124. }
  1125.  
  1126.  
  1127. //----------------------------------------------------------------------------
  1128. //   Description:
  1129. //    Parameters:
  1130. //       Returns:    TRUE if successful.
  1131. //----------------------------------------------------------------------------
  1132. Z4_SUFFIX FN_M Z4_PARSE::Suffix()
  1133. {
  1134.     Z4_SUFFIX suffix = 0;
  1135.  
  1136.     //
  1137.     //    This logic is an attempt to deal with ambiguous addresses.
  1138.     //    Rules:
  1139.     //        1) If 2 words remain in the primary name and the first is a 
  1140.     //           directional and the second is a suffix, only remove the
  1141.     //            suffix if it is a valid abbreviation
  1142.     //
  1143.     //    Example:
  1144.     //                            Pre   Primary    Suffix
  1145.     //        W CENTER        -->  W    CENTER
  1146.     //        W CTR           -->       W            CTR
  1147.     //
  1148.     //
  1149.     if (cStack == 2
  1150.     && BTEST(pelem[0].fs, Z4_P_DIR)
  1151.     && BTEST(pelem[1].fs, Z4_P_SUFFIX))
  1152.         {
  1153.         Z4SuffixFindAbbrev(Word(pelem[1].cWord), &suffix);
  1154.         PCSZ pcszFull = Z4SuffixFull(suffix);
  1155.  
  1156.         if (strcmp(pcszFull, Word(pelem[1].cFull)) == 0)
  1157.             return 0;
  1158.         }
  1159.     //
  1160.     //    Remove a suffix from the end of an address
  1161.     //
  1162.     if (cStack > 1
  1163.     && (pelem[cStack - 1].fs & Z4_P_SUFFIX))
  1164.         {
  1165.         if (Z4SuffixFindAbbrev(Word(pelem[cStack - 1].cWord), &suffix))
  1166.             Delete(cStack - 1, 1);
  1167.         }
  1168.     return suffix;
  1169. }
  1170.  
  1171.  
  1172. //----------------------------------------------------------------------------
  1173. //   Description:    
  1174. //    Parameters:
  1175. //       Returns:    TRUE if successful.
  1176. //----------------------------------------------------------------------------
  1177. BOOL FN_M Z4_PARSE::Zip4()
  1178. {
  1179.     if (cStack >= 3
  1180.     && (pelem[cStack - 3].fs & Z4_P_NUMERIC)
  1181.     && pelem[cStack - 3].cLen == MAX_ZIP5
  1182.     && (pelem[cStack - 2].fs & Z4_P_OTHER)
  1183.     && pelem[cStack - 2].cLen == 1
  1184.     && strcmp(Word(pelem[cStack - 2].cWord) , "-") == 0
  1185.     && (pelem[cStack - 1].fs & Z4_P_NUMERIC)
  1186.     && pelem[cStack - 1].cLen == MAX_ADDON)
  1187.         {
  1188.         strcpy(szZip5, Word(pelem[cStack - 3].cWord));
  1189.         strcpy(szAddon, Word(pelem[cStack - 1].cWord));
  1190.         Delete(cStack - 3, 3);
  1191.         }
  1192.     else if (cStack >= 1
  1193.     && (pelem[cStack - 1].fs & Z4_P_NUMERIC)
  1194.     && pelem[cStack - 1].cLen == MAX_ZIP5)
  1195.         {
  1196.         strcpy(szZip5, Word(pelem[cStack - 1].cWord));
  1197.         szAddon[0] = '\0';
  1198.         Delete(cStack - 1, 1);
  1199.         }
  1200.     else if (cStack >= 1
  1201.     && (pelem[cStack - 1].fs & Z4_P_NUMERIC)
  1202.     && pelem[cStack - 1].cLen == MAX_ZIP4)
  1203.         {
  1204.         strcpy(szZip4, Word(pelem[cStack - 1].cWord));
  1205.         memcpy(szZip5, szZip4, MAX_ZIP5);
  1206.         szZip5[MAX_ZIP5] = '\0';
  1207.         strcpy(szAddon, szZip4 + MAX_ZIP5);
  1208.         Delete(cStack - 1, 1);
  1209.         }
  1210.     if (szZip5[0] && szAddon[0])
  1211.         {
  1212.         strcpy(szZip4, szZip5);
  1213.         strcat(szZip4, szAddon);
  1214.         }
  1215.     return TRUE;
  1216. }
  1217. //----------------------------------------------------------------------------
  1218. //------------------------------- End of File --------------------------------
  1219. //----------------------------------------------------------------------------
  1220.